package com.ldh.mod4j.businessdomain.generator.hibernate

import com.google.inject.Inject
import org.mod4j.dslcommon.generator.helpers.NameMapper
import org.mod4j.businessdomain.generator.helpers.BusinessClassHelpers
import com.ldh.mod4j.businessdomain.businessDomainDsl.BusinessClass
import org.mod4j.dslcommon.xpand.java.JavaComments
import org.mod4j.businessdomain.generator.helpers.BusinessDomainModelHelpers

class DaoImplBase {
	@Inject extension JavaComments javaComments
	
	def daoImplementationBase(BusinessClass clazz)'''
«javaComments.regenerationWarningHeader("Mod4j BusinessDomainDsl generator: DaoImplBase.xtend", clazz)»

package «NameMapper::getDaoImplPackage(BusinessDomainModelHelpers::getModel(clazz).name)»;

import java.sql.SQLException;
import java.util.List;
import java.util.Set;
import java.util.HashSet;

import «NameMapper::getFqnDomainClass(BusinessDomainModelHelpers::getModel(clazz).name, clazz.name)»;
import «NameMapper::getFqnDaoBaseInterface(BusinessDomainModelHelpers::getModel(clazz).name, clazz.name)»;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;
import org.mod4j.runtime.queries.SearchParameters;
«FOR e: BusinessClassHelpers::getToOneAssociationsToInHierarchy(clazz)»
	«val importClass = e.target»
	«IF importClass != clazz»
import «NameMapper::getFqnDomainClass(BusinessDomainModelHelpers::getModel(importClass).name, importClass.name)»;
import «NameMapper::getFqnDaoBaseImplClass(BusinessDomainModelHelpers::getModel(importClass).name, importClass.name)»;
	«ENDIF»
«ENDFOR»
«FOR e: BusinessClassHelpers::getToOneAssociationsFromInHierarchy(clazz)»
	«val importClass = e.source»
	«IF importClass != clazz»
import «NameMapper::getFqnDomainClass(BusinessDomainModelHelpers::getModel(importClass).name, importClass.name)»;
import «NameMapper::getFqnDaoBaseImplClass(BusinessDomainModelHelpers::getModel(importClass).name, importClass.name)»;
	«ENDIF»
«ENDFOR»

/**
 * «NameMapper::javaDomainClass(clazz.name)» Data Access Object (DAO) Implementation. Responsible for Creating,
 * Reading, Updating, Deleting and retrieving lists of «NameMapper::javaDomainClass(clazz.name)» objects in the database.
 * All database access is done here.
 *  
 * @author Generated by Mod4j
 */

public class «NameMapper::javaDaoBaseClass(clazz.name)» extends HibernateDaoSupport implements «NameMapper::javaDaoBaseInterface(clazz.name)» {

    /**
     * {@inheritDoc}
     */
    public «NameMapper::javaDomainClass(clazz.name)» retrieve(Long id) {
        Object o = getHibernateTemplate().get(«NameMapper::javaDomainClass(clazz.name)».class, id);
        if (o == null) {
            return null;
        }
        return («NameMapper::javaDomainClass(clazz.name)») o;
    }

    /**
     * {@inheritDoc}
     */
    public Long add(«NameMapper::javaDomainClass(clazz.name)» object) {
        Long result = (Long) getHibernateTemplate().save(object);
        return result;
    }

    /**
     * {@inheritDoc}
     */
    public void delete(«NameMapper::javaDomainClass(clazz.name)» object ) {
        getHibernateTemplate().delete(object);
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List<«NameMapper::javaDomainClass(clazz.name)»> listAll() {
        return getHibernateTemplate().find("from «NameMapper::javaDomainClass(clazz.name)»");
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List<«NameMapper::javaDomainClass(clazz.name)»> listPage(final int firstResult, final int maxResults) {

        DetachedCriteria criteria = DetachedCriteria.forClass(«NameMapper::javaDomainClass(clazz.name)».class);
        return getHibernateTemplate().findByCriteria(criteria, firstResult, maxResults);
    }  
    
    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List<«NameMapper::javaDomainClass(clazz.name)»> listPage(final int firstResult, final int maxResults,
        final String sortProperty, final boolean isAscending) {
        
        Assert.notNull(sortProperty, "argument [sortProperty] may not be null");
        
        org.hibernate.criterion.Order order;
        if (isAscending) {
            order = org.hibernate.criterion.Order.asc(sortProperty);
        } else {
            order = org.hibernate.criterion.Order.desc(sortProperty);
        }
        DetachedCriteria criteria = DetachedCriteria.forClass(«NameMapper::javaDomainClass(clazz.name)».class).addOrder(order);
        return getHibernateTemplate().findByCriteria(criteria, firstResult, maxResults);
    }
    
    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List<«NameMapper::javaDomainClass(clazz.name)»> listPageByExample(final «NameMapper::javaDomainClass(clazz.name)» example, final int firstResult, final int maxResults,
            final String sortProperty, final boolean isAscending) {
            
        Assert.notNull(example, "argument [example] may not be null");
        Assert.notNull(sortProperty, "argument [sortProperty] may not be null");
        
        List<«NameMapper::javaDomainClass(clazz.name)»> result;
        result = getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(final Session session) throws HibernateException, SQLException {

                org.hibernate.criterion.Order order;
                if (isAscending) {
                    order = org.hibernate.criterion.Order.asc(sortProperty);
                } else {
                    order = org.hibernate.criterion.Order.desc(sortProperty);
                }
                return session.createCriteria(«NameMapper::javaDomainClass(clazz.name)».class).add(
                        Example.create(example).enableLike(MatchMode.ANYWHERE).ignoreCase())
                        .setFirstResult(firstResult).setMaxResults(maxResults).addOrder(order).list();
            }
        });
        return result;
    }

    /**
     * {@inheritDoc}
     */
    public long count() {
        return ((Long) getHibernateTemplate().iterate("select count(*) from «NameMapper::javaDomainClass(clazz.name)»").next()).longValue();
    }
    
    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public long count(final «NameMapper::javaDomainClass(clazz.name)» example) {
    
        Assert.notNull(example, "argument [example] may not be null");

        List<Integer> result;
        result = getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(final Session session)
                    throws HibernateException, SQLException {
                Criteria crit = session.createCriteria(«NameMapper::javaDomainClass(clazz.name)».class);
                crit.setProjection(Projections.rowCount());
                crit.add(Example.create(example).enableLike(MatchMode.ANYWHERE).ignoreCase());
                return crit.list();
            }
        });
        return result.get(0);
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public long countByExample(final «NameMapper::javaDomainClass(clazz.name)» example) {

        Assert.notNull(example, "argument [example] may not be null");

        final SearchParameters parameters = new SearchParameters();
        parameters.setMatchMode(MatchMode.ANYWHERE);
        parameters.setIgnoreCase(Boolean.TRUE);

        List<Integer> result;
        result = getHibernateTemplate().executeFind(new «NameMapper::javaDomainClass(clazz.name)»HibernateCallback(example, parameters, true));
        return result.get(0);
    }
    
    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public long countByExample(final «NameMapper::javaDomainClass(clazz.name)» example, final SearchParameters parameters) {
  
        if (parameters == null) {
            return countByExample(example);
        }
        
        Assert.notNull(example, "argument [example] may not be null");

        List<Integer> result;
        result = getHibernateTemplate().executeFind(new «NameMapper::javaDomainClass(clazz.name)»HibernateCallback(example, parameters, true));
        return result.get(0);
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List<«NameMapper::javaDomainClass(clazz.name)»> findByExample(final «NameMapper::javaDomainClass(clazz.name)» example) {
        
        Assert.notNull(example, "argument [example] may not be null");
        
        SearchParameters parameters = new SearchParameters();
        parameters.setMatchMode(MatchMode.ANYWHERE);
        parameters.setIgnoreCase(Boolean.TRUE);
        
        List<«NameMapper::javaDomainClass(clazz.name)»> result;
        result = getHibernateTemplate().executeFind(new «NameMapper::javaDomainClass(clazz.name)»HibernateCallback(example, parameters));
        return result;
     }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List<«NameMapper::javaDomainClass(clazz.name)»> findByExample(final «NameMapper::javaDomainClass(clazz.name)» example, final SearchParameters parameters) {
        
        if (parameters == null) {
            return findByExample(example);
        }

        Assert.notNull(example, "argument [example] may not be null");

        List<«NameMapper::javaDomainClass(clazz.name)»> result;
        result = getHibernateTemplate().executeFind(new «NameMapper::javaDomainClass(clazz.name)»HibernateCallback(example, parameters));
        return result;
     }

	«IF BusinessClassHelpers::getAllToOneAssociationsInHierarchy(clazz).size > 0»
	    /**
	     * Composes the given <code>criteria</code> object for associations of the given <code>example</code>.
	     * Adds an example object for each of the to-one associations of the <code>example</code> object,
	     * or, if the id is set, adds a restriction based on the id. All eventually existing to-many associations of
	     * <code>example</code> object are ignored.
	     * The <code>addedAssociations</code> set is used to prevent looping in circular associations.
	     * 
	     * @param criteria
	     * @param example
	     * @param associationPath
	     * @param parameters
	     * @param addedAssociations
	     */
	    static void addAssociatedExampleCriteria(Criteria criteria, final «NameMapper::javaDomainClass(clazz.name)» example, final String associationPath,
	     	final SearchParameters parameters, final Set<Object> addedAssociations) {
	     
		    «FOR e: BusinessClassHelpers::getToOneAssociationsToInHierarchy(clazz)»
		        if (example.get«e.targetRoleName.toFirstUpper()»() != null &&!addedAssociations.contains(example.get«e.targetRoleName.toFirstUpper()»())) {
		                    
		            «NameMapper::javaDomainClass(e.target.name)» associatedExample = example.get«e.targetRoleName.toFirstUpper()»();
		            String «e.targetRoleName»Path = associationPath + "«e.targetRoleName»";
		            
		            if (associatedExample.getId() != null) {
		                criteria.add(Restrictions.eq("«e.targetRoleName».id", associatedExample.getId()));
		            } else {
		                criteria.createCriteria(«e.targetRoleName»Path).add(parameters.createExample(associatedExample));
		            }
		            addedAssociations.add(associatedExample);
		            «IF (BusinessClassHelpers::getAllToOneAssociationsInHierarchy(e.target).size > 0)»
		                «NameMapper::javaDaoBaseClass(e.target.name)».addAssociatedExampleCriteria(criteria, associatedExample, «e.targetRoleName»Path + ".", parameters, addedAssociations);
		            «ENDIF»
		        }
		    «ENDFOR»
		    «FOR e: BusinessClassHelpers::getToOneAssociationsFromInHierarchy(clazz)»
		        if (example.get«e.sourceRoleName.toFirstUpper()»() != null && !addedAssociations.contains(example.get«e.sourceRoleName.toFirstUpper()»())) {
		            
		            «NameMapper::javaDomainClass(e.source.name)» associatedExample = example.get«e.sourceRoleName.toFirstUpper()»();
		            String «e.sourceRoleName»Path = associationPath + "«e.sourceRoleName»";
		            
		            if (associatedExample.getId() != null) {
		                criteria.add(Restrictions.eq("«e.sourceRoleName».id", associatedExample.getId()));
		            } else {
		                criteria.createCriteria(«e.sourceRoleName»Path).add(parameters.createExample(associatedExample));
		            }
		            addedAssociations.add(associatedExample);
		            «IF (BusinessClassHelpers::getAllToOneAssociationsInHierarchy(e.source).size > 0)»
		                «NameMapper::javaDaoBaseClass(e.source.name)».addAssociatedExampleCriteria(criteria, associatedExample, «e.sourceRoleName»Path + ".", parameters, addedAssociations);
		            «ENDIF»
		        }
		    «ENDFOR»
	    }
	«ENDIF»
	private static final class «NameMapper::javaDomainClass(clazz.name)»HibernateCallback implements HibernateCallback {
        
	    private final «NameMapper::javaDomainClass(clazz.name)» example;
        private final SearchParameters parameters;
        private final boolean rowcount;

        private «NameMapper::javaDomainClass(clazz.name)»HibernateCallback(«NameMapper::javaDomainClass(clazz.name)» example, SearchParameters parameters) {
            this.example = example;
            this.parameters = parameters;
            this.rowcount = false;
        }
        
        private «NameMapper::javaDomainClass(clazz.name)»HibernateCallback(«NameMapper::javaDomainClass(clazz.name)» example, SearchParameters parameters, boolean rowcount) {
            this.example = example;
            this.parameters = parameters;
            this.rowcount = rowcount;
        }

        public Object doInHibernate(final Session session) throws HibernateException, SQLException {

        	Criteria criteria = session.createCriteria(«NameMapper::javaDomainClass(clazz.name)».class);
        	if (example.getId() != null) {
        		criteria.add(Restrictions
        				.eq("id", example.getId()));
        	} else {
        		criteria.add(parameters.createExample(example));
        	}
        	if (rowcount) {
        	    criteria.setProjection(Projections.rowCount());
        	} else {
			    criteria = parameters.constrainCriteria(criteria);
			}
			«IF (BusinessClassHelpers::getAllToOneAssociationsInHierarchy(clazz).size > 0)»
				addAssociatedExampleCriteria(criteria, example, "", parameters, new HashSet<Object>());
			«ENDIF»
        	return criteria.list();
        }
    }
}
'''
}